#ifndef Ring_QUEUE_HPP_
#define _Ring_QUEUE_HPP_

#include <iostream>
#include <vector>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "sem.hpp"

using namespace std;

const int g_default_num = 5;

// 多生产多消费的意义在哪？
// 不要狭隘的认为，把任务或者数据放在交易场所，就是生产和消费了！
// 将数据或者任务生产前和拿到之后处理，才是最好费时间的。
// 生产的本质：私有的任务 -> 公共空间中
// 消费的本质：公共空间中的任务 -> 私有的
// 拿任务是需要耗费时间的，但是处理的时候是可以并发的！

// 信号量本质是一把计数器 -> 计数器的意义是什么？
// 计数器意义：可以不用进入临界区，就可以得知资源情况，甚至可以减少临界区内部的判断!
// 申请锁 -> 判断与访问 -> 释放锁 -> 本质是我们并不清楚临界资源的情况！
// 信号量要提前预设资源情况，而且在pv变化过程中，我们可以在外部就能知晓临界资源的情况！

template <class T>
class RingQueue
{
public:
    RingQueue(int default_num = g_default_num)
        : ring_queue_(default_num),
          num_(default_num),
          c_step_(0),
          p_step_(0),
          space_sem_(default_num),
          data_sem_(0)
    {
        pthread_mutex_init(&clock, nullptr);
        pthread_mutex_init(&plock, nullptr);
    }
    ~RingQueue() 
    {
        pthread_mutex_destroy(&clock);
        pthread_mutex_destroy(&plock);
    }
    // 生产者：关心空间资源，生产者们的临界资源是什么？下标
    void push(const T &in)
    {
        // 先申请信号量，信号量是原子的，锁的粒度越小越好
        space_sem_.p();
        pthread_mutex_lock(&plock);
        // 到这，一定是竞争成功的生产者线程 -- 就一个！
        // 生产者生产数据需要先申请空间 -- 先p操作
        ring_queue_[p_step_++] = in;
        p_step_ %= num_; // p_step取模后，每次的push位置都是环形队列的下一个位置
        pthread_mutex_unlock(&plock);
        data_sem_.v();   // 数据资源使用完后，必须释放，否则占用空间
    }
    // 消费者：关心数据资源
    void pop(T *out)
    {
        data_sem_.p();
        pthread_mutex_lock(&clock);
        // 消费者要消费必须先申请数据资源，成功才能消费
        // 到这，一定是竞争成功的消费者线程 -- 就一个！
        *out = ring_queue_[c_step_++];
        c_step_ %= num_;
        pthread_mutex_unlock(&clock);
        space_sem_.v(); // 消费完后释放空间资源
    }
    // void debug()
    // {
    //     cerr << "size: " << ring_queue_.size() << ", num: " << num << endl;
    // }

private:
    vector<T> ring_queue_;
    int num_;       // 环形队列大小
    int c_step_;    // 消费下标
    int p_step_;    // 生产下标
    Sem space_sem_; // 空间资源
    Sem data_sem_;  // 数据资源
    pthread_mutex_t clock;
    pthread_mutex_t plock;
};

#endif
